package netsoul.connect;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.URLEncoder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class NetsoulConnect {

	static final String ns_server = "ns-server.epita.fr";
	static final Integer ns_port = 4242;
	private final static String HEX_DIGITS = "0123456789abcdef";

	private INetsoulListener listener;

	private final String login;
	private final String password_socks;

	private Socket ns_socket;
	private PrintWriter send;
	private BufferedReader receive;
	private Boolean listen = false;

	private static NetsoulConnect instance = null;
	
	public final static NetsoulConnect getInstance(final String login, final String password_socks) {
		if (null == instance) {
			instance = new NetsoulConnect(login, password_socks);
		}
		return instance;
	}
	
	public final static NetsoulConnect getInstance() {
		if (null == instance) {
			instance = new NetsoulConnect("test", "test");
		}
		return instance;
	}
	
	private NetsoulConnect(final String login, final String password_socks) {
		this.login = login;
		this.password_socks = password_socks;
	}

	public void setNetsoulListener(final INetsoulListener listener) {
		this.listener = listener;
	}

	public void connect() {
		createSocket();
	}

	public void disconnect() {
		listen = false;
		try {
			if (ns_socket.isConnected())
				ns_socket.close();
		} catch (IOException ex) {
			listener.onNetsoulError(ex);
		}
	}
	
	public boolean isConnected() {
		return listen;
	}

	private void createSocket() {
		Thread create = new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					ns_socket = new Socket(ns_server, ns_port);
					send = new PrintWriter(ns_socket.getOutputStream());
					receive = new BufferedReader(new InputStreamReader(ns_socket.getInputStream()));
					authenticate();
					listen();
					listener.onNetsoulConnect();
				} catch (Exception ex) {
					listener.onNetsoulError(ex);
				}
			}
		});

		create.start();
	}

	private void authenticate() throws NoSuchAlgorithmException, IOException {
		String response;
		String data[];

		response = receive.readLine();
		data = response.split("[ ]+");
		response = data[2] + "-" + data[3] + "/" + data[4] + password_socks;
		byte[] digest = MessageDigest.getInstance("MD5").digest(response.getBytes("UTF-8"));
		sendCommand("auth_ag ext_user none none", true);
		sendCommand("ext_user_log " + login + " " + convertChar(digest) + " " + URLEncoder.encode("Epitech for Android", "UTF-8").replace("+", "%20") + " " + URLEncoder.encode("Epitech for Android", "UTF-8").replace("+", "%20"), true);
		sendCommand("state actif:1010101010", false);
	}

	private String sendCommand(final String command, final boolean wait) {
		send.println(command);
		send.flush();
		if (wait) {
			try {
				return receive.readLine();
			} catch (IOException ex) {
				listener.onNetsoulError(ex);
				return null;
			}
		}
		return null;
	}

	private static String convertChar(byte[] v) {
		StringBuilder sb = new StringBuilder(v.length * 2);

		for (int i = 0; i < v.length; i++) {
			int b = v[i] & 0xFF;
			sb.append(HEX_DIGITS.charAt(b >>> 4)).append(HEX_DIGITS.charAt(b & 0xF));
		}
		return (sb.toString());
	}

	private void listen() {
		Thread listen_thread = new Thread(new Runnable() {

			@Override
			public void run() {
				listen = true;

				while (listen) {
					String buffer;

					try {
						buffer = receive.readLine();
						if (buffer != null) {
							if (buffer.startsWith("ping"))
								sendCommand(buffer, true);
							else if (buffer.startsWith("user_cmd"))
								listener.onNetsoulMessage(buffer);
						}
					} catch (IOException ex) {
						if (listen)
							listener.onNetsoulError(ex);
					}
				}
				listener.onNetsoulDisconnect();
			}
		});

		listen_thread.start();
	}

}
